package org.opens.javaskill.jvm.classloader.encryption;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @Date 2020/10/30 21:09
 * @Created by 99126
 */
public class DecryptClassLoader extends ClassLoader {

    private final String DEFAULT_DIR = "E:\\classloader\\classloader2";

    private String dir = DEFAULT_DIR;

    public DecryptClassLoader() {
        super();
    }

    public DecryptClassLoader(ClassLoader parent) {
        super(parent);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String classPath = name.replace(".", "/");
        File classFile = new File(dir, classPath + ".class");

        if (!classFile.exists()) {
            throw new ClassNotFoundException("the class " + name + " not found");
        }

        byte[] classBytes = loadClassBytes(classFile);

        if (null == classBytes || classBytes.length == 0) {
            throw new ClassNotFoundException("the class " + name + " load failed");
        }

        return this.defineClass(name, classBytes, 0, classBytes.length);
    }

    private byte[] loadClassBytes(File classFile) {
        try (
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                FileInputStream fis = new FileInputStream(classFile);
        ) {
            int data;
            while ((data = fis.read()) != -1) {
                baos.write(data ^ EncryptUtil.ENCRYPT_FACTOR);
            }
            baos.flush();
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }


    public void setDir(String dir) {
        this.dir = dir;
    }

    public static void main(String[] args) {
        DecryptClassLoader decryptClassLoader = new DecryptClassLoader();
        try {
            Class<?> aClass = decryptClassLoader.loadClass("org.opens.javaskill.jvm.classloader.MyLoadObject");
            System.out.println(aClass.getClassLoader());

            Object instance = aClass.newInstance();
            Method method = aClass.getMethod("hello", new Class[]{});
            Object result = method.invoke(instance, new Object[]{});
            System.out.println(result);
        } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | InstantiationException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}
